package zcore

import (
	"fmt"
	"math"
	"strconv"
	"sync"

	"github.com/mars/third_part/fork/zap/internal/bufferpool"
	"go.uber.org/zap/buffer"
	"go.uber.org/zap/zapcore"
)

var _sliceEncoderPool = sync.Pool{
	New: func() interface{} {
		return &sliceArrayEncoder{elems: make([]interface{}, 0, 2)}
	},
}

func getSliceEncoder() *sliceArrayEncoder {
	return _sliceEncoderPool.Get().(*sliceArrayEncoder)
}

func putSliceEncoder(e *sliceArrayEncoder) {
	e.elems = e.elems[:0]
	_sliceEncoderPool.Put(e)
}

// prettyEncoder 是满足业务需求的标准的encoder, 其大部分内容参考于consoleEncoder
// 其格式如下：
// 2022-07-15T20:32:50.284 [INFO] [zap/main.go:46] [traceId=xx12231] -- hello world
type prettyEncoder struct {
	*jsonEncoder
}

func NewPrettyEncoder(cfg zapcore.EncoderConfig) zapcore.Encoder {
	if cfg.ConsoleSeparator == "" {
		cfg.ConsoleSeparator = " "
	}
	return prettyEncoder{newJSONEncoder(cfg, false)}
}

func (c prettyEncoder) Clone() zapcore.Encoder {
	return prettyEncoder{c.jsonEncoder.Clone().(*jsonEncoder)}
}

func (c prettyEncoder) EncodeEntry(ent zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) {
	line := bufferpool.Get()
	arr := getSliceEncoder()
	c.EncodeTime(ent.Time, arr)
	c.EncodeLevel(ent.Level, arr)
	arr.AppendString("[" + ent.Caller.TrimmedPath() + "]")
	for i := range arr.elems {
		if i > 0 {
			line.AppendString(c.ConsoleSeparator)
		}
		fmt.Fprint(line, arr.elems[i])
	}
	putSliceEncoder(arr)
	line.AppendString(c.ConsoleSeparator)
	// Add any structured context.
	c.writeContext(line, fields)
	line.AppendString("--")
	line.AppendString(c.ConsoleSeparator)

	line.AppendString(ent.Message)
	line.AppendString(zapcore.DefaultLineEnding)
	return line, nil
}

func (c prettyEncoder) writeContext(line *buffer.Buffer, extra []zapcore.Field) {
	if len(extra) == 0 {
		return
	}
	line.AppendByte('[')
	prettyAddFields(line, extra)
	line.AppendByte(']')
	line.AppendString(c.ConsoleSeparator)
}

func prettyAddFields(line *buffer.Buffer, extra []zapcore.Field) {
	for i, field := range extra {
		if i > 0 {
			line.AppendByte('&')
		}
		line.AppendString(field.Key)
		line.AppendByte('=')
		line.AppendString(parseField(field))
	}
}

func parseField(field zapcore.Field) string {
	switch field.Type {
	case zapcore.BoolType:
		if field.Integer == 1 {
			return "true"
		}
		return "false"
	case zapcore.Float64Type, zapcore.Float32Type:
		return strconv.FormatFloat(math.Float64frombits(uint64(field.Integer)), 'f', 10, 64)
	case zapcore.Int64Type, zapcore.Uint64Type:
		return strconv.FormatInt(int64(field.Integer), 10)
	case zapcore.Int32Type, zapcore.Int16Type, zapcore.Int8Type, zapcore.Uint32Type,
		zapcore.Uint16Type, zapcore.Uint8Type, zapcore.UintptrType:
		return strconv.Itoa(int(field.Integer))
	case zapcore.StringType:
		return field.String
	default:
		return fmt.Sprintf("%+v", field)
	}
}
